﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Core.IO;
using Core.Util;

using ProjNet.CoordinateSystems;
using ProjNet.CoordinateSystems.Transformations;

using SharpMap;
using SharpMap.Base;
using SharpMap.Data.Providers;

using GeoAPI.Geometries;
using GeoAPI.CoordinateSystems;
using GeoAPI.CoordinateSystems.Transformations;
using SharpMap.Layers;

using GPSCrowdSource.Algorithms;

namespace GPSCrowdSource.DataStructures {
  
  //--------------- BROKEN -------------------------------
  /// <summary>
  /// Provides a data source that can read data from any .shp ESRI file.
  /// </summary>
  public class ShapeFileDataSource : GPSListDataSource {

    #region Private
    private List<String> myColumns = new List<string>();
    private ICoordinateTransformation myTransform;
    #endregion

    #region Private Methodes

    /// <summary>
    /// Create a mercator transform from the given
    /// </summary>
    private ICoordinateTransformation CreateMercator(ICoordinateSystem inSys) {
      
      CoordinateSystemFactory cFac = new CoordinateSystemFactory();
      List<ProjectionParameter> parameters = new List<ProjectionParameter>();
      parameters.Add(new ProjectionParameter("latitude_of_origin", 0));
      parameters.Add(new ProjectionParameter("central_meridian", 0));
      parameters.Add(new ProjectionParameter("false_easting", 0));
      parameters.Add(new ProjectionParameter("false_northing", 0));
      IProjection projection = cFac.CreateProjection("Mercator", "Mercator_2SP", parameters);
      
      IProjectedCoordinateSystem coordsys = cFac.CreateProjectedCoordinateSystem("Mercator", ((ProjectedCoordinateSystem)inSys).GeographicCoordinateSystem, projection, LinearUnit.Metre, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North));
      return new CoordinateTransformationFactory().CreateFromCoordinateSystems(inSys, coordsys);
      
      //IGeographicCoordinateSystem fromGeoSystem;
      //if(inSys is IGeographicCoordinateSystem)
      //  fromGeoSystem = inSys as IGeographicCoordinateSystem;
      //else if(inSys is ProjectedCoordinateSystem)  {
      //  ProjectedCoordinateSystem proj = (ProjectedCoordinateSystem)inSys;
      //  fromGeoSystem = proj.GeographicCoordinateSystem;
      //  }
      //else {
      //  //Create geographic coordinate system based on the WGS84 datum
      //  IEllipsoid ellipsoid = cFac.CreateFlattenedSphere("WGS 84", 6378137, 298.257223563, LinearUnit.Metre);
      //  IHorizontalDatum datum = cFac.CreateHorizontalDatum("WGS_1984", DatumType.HD_Geocentric, ellipsoid, null);
      //  IGeographicCoordinateSystem gcs = cFac.CreateGeographicCoordinateSystem("WGS 84", AngularUnit.Degrees, datum,PrimeMeridian.Greenwich, new AxisInfo("Lon", AxisOrientationEnum.East), new AxisInfo("Lat", AxisOrientationEnum.North));
      //  fromGeoSystem = gcs;
      //  }
      
      //IProjectedCoordinateSystem coordsys = cFac.CreateProjectedCoordinateSystem("Mercator", fromGeoSystem, projection, LinearUnit.Metre, new AxisInfo("East", AxisOrientationEnum.East), new AxisInfo("North", AxisOrientationEnum.North));
      //return new CoordinateTransformationFactory().CreateFromCoordinateSystems(inSys, coordsys);
      }

    /// <summary>
    /// Load a geometry and returns an array of gometries found.
    /// </summary>
    private List<GeoPosition> LoadShape(IGeometry inG) {
      List<GeoPosition[]> pos = new List<GeoPosition[]>();

      switch(inG.OgcGeometryType) {

        case OgcGeometryType.MultiLineString:
          for(int k=0;k<inG.NumGeometries;k++) 
            pos.AddRange(LoadShape(inG.GetGeometryN(k)));
          break;
        case OgcGeometryType.Polygon:
        case OgcGeometryType.LineString:
          List<GeoPosition> line = new List<GeoPosition>();
          
          for(int i=0;i<inG.NumGeometries;i++) {
            IGeometry geom = inG.GetGeometryN(i);
            Coordinate c = myTransform.MathTransform.Transform(geom.Coordinate);
            line.Add(new GeoPosition((float)c.X,(float)c.Y));
            }

          pos.Add(line.ToArray());
          break;
        case OgcGeometryType.MultiPoint:
          break;
        case OgcGeometryType.Point:
          //pos.Add(new GeoPosition[] { new GeoPosition((float)cc.X,(float)cc.Y)});
          break;
        case OgcGeometryType.MultiPolygon:
          for(int i=0;i<inG.NumGeometries;i++)
            pos.AddRange(LoadShape(inG.GetGeometryN(i)));
          break;
        default:
          throw new NotImplementedException("Unsupported shape type : " + inG.OgcGeometryType);
        }
      
      return pos;
      }

    #endregion

    #region Constructor

    /// <summary>
    /// Static constructor initializes the geom services.
    /// </summary>
    static ShapeFileDataSource() {
      GeoAPI.GeometryServiceProvider.Instance = new NetTopologySuite.NtsGeometryServices();
      }

    /// <summary>
    /// Create a file data source that can read from a shape file.
    /// </summary>
    public ShapeFileDataSource(FilePath inFile) {
      
      try {
        SharpMap.Data.Providers.ShapeFile sf = new ShapeFile(inFile.ToString(),true);
        myTransform = CreateMercator(sf.CoordinateSystem);

        sf.Open();
        
        int fc = sf.GetFeatureCount();
        
        for(int i=0;i<fc;i++) {
          SharpMap.Data.FeatureDataRow row = sf.GetFeature((uint)i);

          if(!row.IsFeatureGeometryNull()) {
            IGeometry geom = row.Geometry;
            List<GeoPosition> geos = LoadShape(geom);
            base.myPos.AddRange(geos);
            }
          }

        sf.Close();
        }
      catch(Exception ex) {
        Log.Error(ex);
        }
      }

    #endregion

    public String[] Columns {
      get {
        return null;
        }
      }
    }

  }
